Polymorphic Executables
Prelude
I was recommended this video on YouTube, and I found it very interesting and useful, so here I am writing about it. But what are polymorphic executables, you may ask? Polymorphic executables are scripts or programs that can change their behavior based on how they're invoked. This means creating a script that checks its own invocation name ($0
first argument in the executable which is the executable itself, more here) and performs different actions accordingly.
Batman and Robin
I think it will be easier if I show you how it works. So here is an example of how this executable is polymorphic:
1. First, create the script
#!/bin/bash
# use basename to not have to deal with paths
case $(basename "$0") in
batman)
echo "I'm Batman!";
;;
robinDG)
echo "And I'm robin (dick grayson)";
;;
robinTD)
echo "And I'm robin (tim drake)";
;;
robinDW)
echo "And I'm robin (damian wayne)";
;;
*)
echo "catch all [ $0 ]!"
;;
esac
Here we use a code
to check the value of $0
. Depending on that name, it outputs a different message.
2. Make it executable
$ chmod +x batman
3. When we run it directly, we get
$ ./batman
I'm Batman!
4. Now, let's create symbolic links to the same script but with different names
$ ln -s batman robinDG
$ ln -s batman robinTD
$ ln -s batman robinDW
$
$ ls -l
total 8
-rwxr-xr-x@ 2 hp staff 348 12 Mar 14:28 batman
lrwxr-xr-x@ 1 hp staff 6 12 Mar 13:35 robinDG -> batman
lrwxr-xr-x@ 1 hp staff 6 12 Mar 13:36 robinDW -> batman
lrwxr-xr-x@ 1 hp staff 6 12 Mar 13:35 robinTD -> batman
5. When we run the script through these different symlinks
$ ./robinDG
And I'm robin (dick grayson)
$ ./robinTD
And I'm robin (tim drake)
$ ./robinDW
And I'm robin (damian wayne)
Each invocation produces different output, even though they're all pointing to the same executable!
How/Where do I use this
$ ls
lrwxr-xr-x@ - hp 1 week blender_render -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week cn -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week compress_pdf -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week convert_to_webp -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week crd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week gacp -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week gcd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week lcm -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week mkcd -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 8 months nvim -> /Users/hp/Developer/tools/neovim/build/bin/nvim
lrwxr-xr-x@ - hp 1 week openssl_dec -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week openssl_enc -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week print_colors -> /Users/hp/dotfiles/bin/utils
lrwxr-xr-x@ - hp 1 week remove_ds -> /Users/hp/dotfiles/bin/utils
Practical Applications
BusyBox-Style Utilities
The popular BusyBox utility uses this exact pattern. BusyBox is a single executable that provides simplified versions of many common UNIX utilities. When symlinked as `ls`, `cp`, `mv`, etc., it behaves accordingly. The BusyBox source code shows how this is implemented in C.
Multi-Purpose Scripts
Create a single maintenance script that performs different functions based on how it's called:
#!/bin/bash
case $(basename "$0") in
backup)
# Backup functionality
;;
restore)
# Restore functionality
;;
cleanup)
# Cleanup functionality
;;
esac
Installation Scripts
A single script could handle installation, uninstallation, and updates based on symlinks named `install`, `uninstall`, and `update`. The Git Project uses a similar approach for some of its utilities.